/*******************************************************************

   DOExchange.c
   
   It's a try to do the Exchange work within a dopus popup menu.
   
   Since nothing of the commodities.library is really documented,
   this source may be wrong. If you know more, you may change/add
   what is to do...
   
   SO BE WARNED... - THIS IS A HACK !!
   (even if it seems to be OK)
   
   Initial idea by Jens Weyer. 
   
*********************************************************************/

#include "includes/DOExchange.h"

/********************************************************************/
// some prototypes
 
ULONG __asm __saveds New_Proc_Startup( register __a0 IPCData *ipc,
                                       register __a1 DOE_Data *dd );

void __saveds New_Proc( void );
void CleanUp( DOE_Data *dd, IPCData *ipc );

BOOL OpenWin( DOE_Data *dd );
void BorderDraw( DOE_Data *dd, BOOL invers );

BOOL InitPopupMenu( DOE_Data *dd );
APTR CreateSubMenu( DOE_Data *dd, ULONG from, ULONG to );
void ClearPopupMenu( DOE_Data *dd );
BOOL DoMenu( DOE_Data *dd );

BOOL HandleWin( DOE_Data *dd );
BOOL HandleCX( DOE_Data *dd );
BOOL HandleNotify( DOE_Data *dd );
BOOL HandleIPC( DOE_Data *dd );

BOOL SendCxMsg( CX_OBJ *cxobj, ULONG type, struct MsgPort *reply_to );

BOOL WriteConfig( DOE_Data *dd );
BOOL ReadConfig( DOE_Data *dd );

/********************************************************************/
// it is surely useful to detach here

void DOExchange( STRPTR args, struct Screen *screen, IPCData *ipc )
{
     DOE_Data *dd;
     FuncArgs *fargs;
      
     if( !exchange )
       {
          if( (dd = AllocMemH(mempool, sizeof(DOE_Data))) )
            {
               dd->screen  = screen;
               dd->a4      = getreg( REG_A4 );
               dd->module  = (struct Library *) getreg( REG_A6 );
               dd->library = DOpusBase;
          
               IPC_Launch( 0, 
                           &exchange,         // pointer to pointer to the IPC of the new process, for storing...
                           "DOpus Exchange",  // name of the new process
                           (ULONG) New_Proc,  // entrypoint of the new process
                           4096,              // stack to use
                           (ULONG) dd,        // data to pass to the new process
                           DOSBase );         // pointer to dos.library
                         
               if( !exchange )                // if detaching failed...
                 {
                    FreeMemH( dd );
                    return;
                 }
            }
       }
       
     if( args )
       {  
          if( (fargs = ParseArgs(FUNC0_TEMPLATE, args)) )
            {
               ULONG cmd = 0, counter;
               PassData *pd;
               
               for( counter = 0; counter < TEMPLATE_COUNT; counter++ )
                    if( fargs->FA_Arguments[counter] )
                         cmd |= 1 << counter;
               
               if( (pd = AllocVec(sizeof(PassData), MEMF_CLEAR)) )
                 {
                    pd->FrontPen = ARG_TC   ? *(ULONG *) ARG_TC   : NULL;
                    pd->BackPen  = ARG_BGC  ? *(ULONG *) ARG_BGC  : NULL;
                    pd->name     = ARG_NAME ? (STRPTR)   ARG_NAME : NULL;
                    
                    IPC_Command( exchange, 
                                 cmd,
                                 NULL,
                                 NULL,
                                 pd,              // FreeVec the data automatically
                                 REPLY_NO_PORT );
                  }
                                              
               DisposeArgs( fargs );
             }
       }      
}

/********************************************************************/

ULONG __asm __saveds New_Proc_Startup( register __a0 IPCData *ipc,
                                       register __a1 DOE_Data *dd )
{     
        struct Library *DOpusBase; 

        putreg( REG_A4, dd->a4 );
        dd->ipc = ipc;
        DOpusBase = dd->library;

        // now follows our initialization
        
        dd->module->lib_OpenCnt++;
        
        // the default button text
        dd->itext.IText = DOpusGetString( locale, MSG_TEXT );
        dd->itext.FrontPen = 1;
        
        ReadConfig( dd );
        
        if( strlen(dd->name) )
             dd->itext.IText = dd->name;
                
        // install our notify
        if( !(dd->notify_port   = CreateMsgPort()) ||
            !(dd->notify_handle = AddNotifyRequest(DN_OPUS_HIDE | DN_OPUS_SHOW | DN_OPUS_QUIT |
                                                   DN_CLOSE_WORKBENCH | DN_OPEN_WORKBENCH | DN_RESET_WORKBENCH,
                                                   NULL, dd->notify_port)) )
             return FALSE;
                    
        // fill our newbroker structure
        dd->nb.nb_Version = NB_VERSION;
        dd->nb.nb_Name    = DOpusGetString( locale, FUNC0_DESCRIPTION );
        dd->nb.nb_Title   = dd->nb.nb_Name;
        dd->nb.nb_Descr   = DOpusGetString( locale, MSG_CX_DESCR );
        dd->nb.nb_Unique  = NBU_UNIQUE|NBU_NOTIFY;
        dd->nb.nb_Flags   = COF_SHOW_HIDE;
        
        // create the broker              
        if( !(dd->nb.nb_Port = CreateMsgPort()) ||
            !(dd->our_cxobj  = CxBroker(&dd->nb, (LONG *) &dd->signals)) ||
            dd->signals      ||
            !(dd->sender     = CreateMsgPort()) )
             return FALSE; 
                    
        return TRUE; // all was successfully
} 

void __saveds New_Proc( void )
{
     DOE_Data *dd;
     IPCData *ipc;
     struct Library *DOpusBase;
           
     if( !(DOpusBase = (struct Library *) FindName(&((struct ExecBase *)*((ULONG *)4))->LibList, "dopus5.library")) )
          return;
     
     ipc = IPC_ProcStartup( (ULONG *) &dd, New_Proc_Startup );
     putreg( REG_A4, dd->a4 );
     
     if( !ipc )
       {
          if( dd )
               CleanUp( dd, dd->ipc );
                      
          return;
       }
          
     // make our broker aktive
     ActivateCxObj( dd->our_cxobj, TRUE );
                 
     // open the window (looks like a dopus button)            
     if( OpenWin(dd) )
       {                 
          while( TRUE )
            {
               dd->signals = Wait( 1 << dd->sender->mp_SigBit            |                 // reply port of our sended messages
                                   1 << dd->nb.nb_Port->mp_SigBit        |
                                   1 << dd->notify_port->mp_SigBit       |                                 
                                   1 << dd->ipc->command_port->mp_SigBit |                 // ipc messages
                                   (dd->win ? 1 << dd->win->UserPort->mp_SigBit : NULL) ); // IDCMP messages
               
               if( dd->signals & 1 << dd->sender->mp_SigBit ) // our message does return
                 {
                    while( (dd->fmsg = (FakeMsg *) GetMsg(dd->sender)) )
                         FreeMemH( dd->fmsg );                // we have only to free the memory
                 }
                 
               dd->stop = FALSE;
                         
               if( dd->signals & 1 << dd->nb.nb_Port->mp_SigBit )
                 {
                    if( HandleCX(dd) )
                         break;
                 } 
                 
               if( dd->signals & 1 << dd->notify_port->mp_SigBit )
                 {
                    if( HandleNotify(dd) )
                         break;                    
                 } 
                 
               if( dd->signals & 1 << dd->ipc->command_port->mp_SigBit )
                 {
                    if( HandleIPC(dd) )
                         break;
                 } 
               
               if( dd->win && (dd->signals & 1 << dd->win->UserPort->mp_SigBit) )
                 {
                    if( HandleWin(dd) )
                         break;
                 }            
            }       
       }
   
     CleanUp( dd, exchange );
     exchange = NULL;       
}

void CleanUp( DOE_Data *dd, IPCData *ipc )
{
     if( dd->win )
          CloseWindow( dd->win );
               
     if( dd->notify_port )
       {
          if( dd->notify_handle )
               RemoveNotifyRequest( dd->notify_handle );
                    
          while( !IsMsgPortEmpty( dd->notify_port) )
               ReplyFreeMsg( GetMsg(dd->notify_port) );
                         
          DeleteMsgPort( dd->notify_port );
       }
       
     DeleteCxObjAll( dd->our_cxobj );
     
     if( dd->nb.nb_Port )
       {
          while( !IsMsgPortEmpty(dd->nb.nb_Port) )
               ReplyMsg( GetMsg(dd->nb.nb_Port) );
          
          DeleteMsgPort( dd->nb.nb_Port );
       }
       
     if( dd->sender )
       {
          while( !IsMsgPortEmpty(dd->sender) )
               FreeMemH( GetMsg(dd->sender) );
          
          DeleteMsgPort( dd->sender );
       }
   
     dd->module->lib_OpenCnt--;
     
     FreeMemH( dd );
     
     IPC_Free( ipc );
     
}

/********************************************************************/

BOOL OpenWin( DOE_Data *dd )
{    
     // lock the screen   
     if( (dd->screen = LockPubScreen(NULL)) )
       {
          if( (dd->win = OpenWindowTags(  NULL,
                                          WA_Left,   dd->ibox.Left,
                                          WA_Top,    dd->ibox.Top,
                                          WA_Height, dd->screen->Font->ta_YSize + 4,
                                          WA_Width,  IntuiTextLength( &dd->itext ) + 5,
                                    
                                          WA_Gadgets, &draggadget,
                                    
                                          WA_Flags,   WFLG_BORDERLESS | WFLG_NEWLOOKMENUS |
                                                      WFLG_SMART_REFRESH | WFLG_RMBTRAP,
                                                
                                          WA_IDCMP,   IDCMP_MOUSEBUTTONS,
                                                
                                          WA_AutoAdjust, TRUE,                                  
                                          WA_PubScreen, dd->screen,
                                          WA_ScreenTitle, (STRPTR) ~0,
                                          TAG_DONE)) )
            {  
               // register our window, so it does get messages
               // even it is inactive
               SetWindowID( dd->win, &dd->id, 0x1400, 0 );
              
               BorderDraw( dd, FALSE );          
            }
     
          UnlockPubScreen( NULL, dd->screen );
       }
           
     return (BOOL) dd->win;     
}

void BorderDraw( DOE_Data *dd, BOOL invers )
{     
   // flood the port with supplied colour
   SetRast( dd->win->RPort, dd->itext.BackPen );
   
   // draw a border         
   SetAPen( dd->win->RPort, invers ? 2 : 1 );
   Move( dd->win->RPort, 1, dd->win->Height - 1 );
   Draw( dd->win->RPort, dd->win->Width-1, dd->win->Height - 1 );
   Draw( dd->win->RPort, dd->win->Width-1, 1 );
     
   SetAPen( dd->win->RPort, invers ? 1 : 2 );
   Move( dd->win->RPort, 0, dd->win->Height - 1 );
   Draw( dd->win->RPort, 0, 0 );
   Draw( dd->win->RPort, dd->win->Width-1, 0 );  
   
   // set values for the Rastport
   SetRPAttrs( dd->win->RPort, 
               RPTAG_APen, dd->itext.FrontPen,
               RPTAG_BPen, dd->itext.BackPen,
               TAG_DONE );
                 
   // print the button text
   Move( dd->win->RPort, 3, dd->win->RPort->Font->tf_Baseline + 2 );
   Text( dd->win->RPort, dd->itext.IText, strlen(dd->itext.IText) );    
}

/********************************************************************/
// popup menu work

// do not be worry, most of this things is only needed to allow:
// - unlimited count of entries with their needed submenu
// - easier to do the cleanup

BOOL InitPopupMenu( DOE_Data *dd )
{
     dd->entry_count = 0;
     
     // init the lists we need     
     NewList( (struct List *) &dd->popmenu.item_list );
     NewList( (struct List *) &dd->submenus );
     
     // open the popupmenu above our window
     dd->popmenu.flags  = POPUPMF_ABOVE;
     
     // locale pointer, so we must not supply real strings - only ID's
     dd->popmenu.locale = locale;
     
     // since nothing is documented here, I have done what I think:
     // all brokers must be anywhere in a list and if we have one
     // node (our broker), we can access the whole list...
     dd->ptr_node = (struct Node *) dd->our_cxobj;
     
     // the objects does work with interrupts, it may a good idea
     // to protect the access to the list with Disable()
     Disable();
     
     // go to the head of the list
     do
       {
          dd->ptr_node = dd->ptr_node->ln_Pred;
       }
     while( dd->ptr_node->ln_Pred->ln_Pred );    
   
     // now we can create our needed lists - we have here 2 + X (!)
     // first list         - MinList with the names of the objects (needed by DoPopUpMenu)
     // second list        - MinList, each node contain again a MinList (third list)
     //                      this list is only for easy handling for us
     // third (and X) list - MinList for submenu, it does also contain a pointer
     //                      to the cx_object
      
     do
       {
          // this counter is used to give each (main) menu item an ID.
          // since these items have all submenus, you may also give
          // all the same ID (dopus5.library does only pass them through) 
          dd->entry_count++;
                              
          // now it's time to create the menu entry...
          if( (dd->popitem = AllocMemH(mempool, sizeof(PopUpItem))) )
            {                    
               dd->popitem->item_name = dd->ptr_node->ln_Name;
               dd->popitem->id        = dd->entry_count;
               
               // each item has a submenu
               if( (dd->popitem->data  = CreateSubMenu(dd, MSG_ACTIVATE, MSG_REMOVE)) )
                    dd->popitem->flags = POPUPF_SUB;
                              
               // the next block is a kind of sorted adding of our nodes
               // since this are MinList's, it is not possible to use DOpus
               // list functions here...
               
               // at first we need a pointer to the first node
               dd->compare = (PopUpItem *) dd->popmenu.item_list.mlh_Head;
               
               if( IsListEmpty((struct List *) &dd->popmenu.item_list) ||  
                   Stricmp(dd->popitem->item_name, dd->compare->item_name) < 0 ) // or our new name is lower
                 {
                    AddHead( (struct List *) &dd->popmenu.item_list,
                             (struct Node *) dd->popitem );
                 }
               else
                 {
                    // walk through the list until a name is higher than our new name or
                    // the list is no more entries                 
                    while( dd->compare->node.mln_Succ->mln_Succ &&
                           Stricmp(dd->popitem->item_name, ((PopUpItem *) dd->compare->node.mln_Succ)->item_name) > 0 )
                            dd->compare = (PopUpItem *) dd->compare->node.mln_Succ;
                  
                    // insert the new node before (!) the higher name is coming
                    // or even append it, if no higher entry does exist
                    Insert( (struct List *) &dd->popmenu.item_list,
                            (struct Node *) dd->popitem,
                            (struct Node *) dd->compare );                
                 }                  
            } 
       }
     while( (dd->ptr_node = dd->ptr_node->ln_Succ)->ln_Succ ); 
   
     // we have not all done now, but we must not access longer the object list,
     // so we can allow again all stuff       
     Enable();
     
     // adding a barlabel
     if( (dd->popitem = AllocMemH(mempool, sizeof(PopUpItem))) )
       {
          dd->popitem->item_name = POPUP_BARLABEL;
          dd->popitem->id = 0;
                                 
          AddTail( (struct List *) &dd->popmenu.item_list,
                   (struct Node *) dd->popitem );              
       } 
     
     // adding a item to snap the current settings  
     if( (dd->popitem = AllocMemH(mempool, sizeof(PopUpItem))) )
       {
          dd->popitem->item_name = (STRPTR) MSG_SAVE_POS;
          dd->popitem->id        = POPID_SAVE;
          dd->popitem->flags     = POPUPF_LOCALE;
                        
          AddTail( (struct List *) &dd->popmenu.item_list,
                   (struct Node *) dd->popitem );              
       }
     
     // and an item to allow to quit directly
     if( (dd->popitem = AllocMemH(mempool, sizeof(PopUpItem))) )
       {
          dd->popitem->item_name = (STRPTR) MSG_QUIT;
          dd->popitem->id        = POPID_QUIT;
          dd->popitem->flags     = POPUPF_LOCALE;
                        
          AddTail( (struct List *) &dd->popmenu.item_list,
                   (struct Node *) dd->popitem );              
       } 
     
     return TRUE;
}

// this routine is already prepared to be copied if you need it :)
// you must change only small things then...

APTR CreateSubMenu( DOE_Data *dd, ULONG from, ULONG to )
{
     PopUpItem *popitem;
     
     if( (dd->submenu_node = AllocMemH(mempool, sizeof(SubMenu_Node))) )
       {
          // init the list
          NewList( (struct List *) &dd->submenu_node->submenu );
               
          // getting the items to show
          for( dd->a4 = from; dd->a4 <= to; dd->a4++ )
            {
               // allocate a node 
               if( (popitem = AllocMemH(mempool, sizeof(PopUpItem))) )
                 {
                    // fill the node
                    popitem->item_name = (STRPTR) dd->a4; 
                    popitem->flags     = POPUPF_LOCALE;
                    popitem->id        = dd->a4;
                    popitem->data      = dd->ptr_node; // pointer to the CX_object
                    
                    switch( dd->a4 )
                      {
                         case MSG_ACTIVATE :
                                             popitem->flags |= POPUPF_CHECKIT;
                                             
                                             // now it is again result of hard work and
                                             // may be wrong... :)
                                             // check the current activation state
                                             //if( ((STRPTR) dd->ptr_node)[0x0E] & 1 << 1 )
                                             if( ((CX_OBJ *) dd->ptr_node)->flags & 1 << 1 )
                                                  popitem->flags |= POPUPF_CHECKED;
                                             
                                             break;
                                             
                         case MSG_APPEAR   :
                         case MSG_DISAPPEAR:
                                             // checking if this object has a GUI
                                             //if( !(((STRPTR) dd->ptr_node)[0x0E] & 1 << 2) )
                                             if( !(((CX_OBJ *) dd->ptr_node)->flags & 1 << 2) )
                                                  popitem->flags |= POPUPF_DISABLED;
                      }
                         
                    // add the popitem node to the submenu list
                    AddTail( (struct List *) &dd->submenu_node->submenu,
                             (struct Node *) popitem );
                 }
            }
          // add the submenu list to the second list, so we can free it later all easily  
          AddTail( (struct List *) &dd->submenus,
                   (struct Node *) dd->submenu_node );
          
          return &dd->submenu_node->submenu;
       }
       
     return NULL;     
}

// this routines should be clear for everyone...
void ClearPopupMenu( DOE_Data *dd )
{
     while( !IsListEmpty((struct List *) &dd->popmenu.item_list) )
          FreeMemH( RemHead((struct List *) &dd->popmenu.item_list) );
     
     while( !IsListEmpty((struct List *) &dd->submenus) )
       {
          dd->submenu_node = (SubMenu_Node *) RemHead((struct List *) &dd->submenus);
          
          while( !IsListEmpty((struct List *) &dd->submenu_node->submenu) )
               FreeMemH( RemHead((struct List *) &dd->submenu_node->submenu) );
            
          FreeMemH( dd->submenu_node );
       }       
}

BOOL DoMenu( DOE_Data *dd )
{
     // preparing the popupmenu
     if( InitPopupMenu(dd) )
       {
          // start the popupmenu
          if( DoPopUpMenu(dd->win, &dd->popmenu, &dd->popitem, MENUDOWN) != -1 )
            {
               // if the previous call does return, dd->popitem contains the user selection
               switch( dd->popitem->id )
                 {
                    case POPID_SAVE : 
                                                WriteConfig( dd );
                                                break;
                                          
                    case POPID_QUIT : 
                                                dd->stop = TRUE;
                                                break;
                  
                    case MSG_ACTIVATE:     
                    case MSG_APPEAR:     
                    case MSG_DISAPPEAR:
                    case MSG_REMOVE:    // create a fake CxMsg (not documented...)
                                        // and send it to the choosed CxObj
                                                
                                        SendCxMsg( (CX_OBJ *) dd->popitem->data, // send it to...
                                                   (dd->popitem->id == MSG_ACTIVATE ? (dd->popitem->flags & POPUPF_CHECKED ? CXCMD_ENABLE : CXCMD_DISABLE) : (dd->popitem->id - MSG_ACTIVATE)*2 + 17 ),
                                                   dd->sender ); // use this port to reply to
                                               
                                        break;
                    }
               }   
             // free the menu
             ClearPopupMenu( dd );                 
       }
       
     return dd->stop;      
}

/********************************************************************/
// handling routines

BOOL HandleWin( DOE_Data *dd )
{        
   while( !dd->stop && (dd->messages = GetMsg(dd->win->UserPort)) )
     {
        // since we are not interested in other messages, we use a simple "if"
        if( ((struct IntuiMessage *) dd->messages)->Class == IDCMP_MOUSEBUTTONS &&
            ((struct IntuiMessage *) dd->messages)->Code == MENUDOWN )
            //&&
            //((ULONG) WhichLayer(&dd->screen->LayerInfo, dd->screen->MouseX, dd->screen->MouseY)) == ((ULONG) dd->win->WLayer) )
          { 
             // since we have no real gadget, the window must be activated
             // else the popup menu may be in some cases not selectable
             ActivateWindow( dd->win );
                               
             // make our window "selected"
             BorderDraw( dd, TRUE );
             
             dd->stop = DoMenu( dd );
             
             // "unselect"           
             BorderDraw( dd, FALSE );
             
             // deactivate our window again (will activate the previous
				 // activated window, if available)
				 
             if( dd->win->NextWindow )
				      ActivateWindow( dd->win->NextWindow );
				 else
				      dd->win->Flags ^= WFLG_WINDOWACTIVE;           
          } 
        ReplyMsg( dd->messages );        
     }     
   
   return dd->stop;
}

BOOL HandleCX( DOE_Data *dd )
{  
   while( !dd->stop && (dd->messages = GetMsg(dd->nb.nb_Port)) )
     {
        // on this place we can use some variables for other work...
        dd->a4      = CxMsgType( (CxMsg *) dd->messages );
        dd->signals = CxMsgID((CxMsg *) dd->messages);
        
        // should be so fast as possible...
        ReplyMsg( dd->messages );
                       
        if(  dd->a4 == CXM_COMMAND )
          {                 
             switch( dd->signals )
               {
                  case CXCMD_DISABLE:    // since we are do not handle any
                                         // other things than our window, it
                                         // is a little bit useless to disable/
                                         // enable, but anyway we do it... :)
                                         ActivateCxObj( dd->our_cxobj, FALSE );
                                         break;
                  
                  case CXCMD_ENABLE:    
                                         ActivateCxObj( dd->our_cxobj, TRUE );
                                         break;
                  
                  case CXCMD_APPEAR:     
                                         if( !dd->win )
                                           {
                                              OpenWin( dd );                                                                                           
                                           }
                                         else
                                              WindowToFront( dd->win );
                                         break;
                  
                  case CXCMD_DISAPPEAR:  
                                         if( dd->win )
                                           {
                                              // save our current position temporally
                                              dd->ibox.Left = dd->win->LeftEdge;
                                              dd->ibox.Top  = dd->win->TopEdge;
                                              
                                              CloseWindow( dd->win );
                                              dd->win = NULL;                                             
                                           }
                                         break;
                  
                  case CXCMD_KILL:      
                                         dd->stop = TRUE;
                                         break;
                                         
                                         
                  /* it seems this value needs a special flag in
                     the newbroker structure and so no messages of
                     this ID does arrive here...
                                         
                  case CXCMD_LIST_CHG:
                                         
                                         break;                                       
                  */                
               }            
          }         
     }
   
   return dd->stop;
}

BOOL HandleNotify( DOE_Data *dd )
{
     while( !dd->stop && (dd->messages = GetMsg(dd->notify_port)) )
     {
        switch( ((DOpusNotify *) dd->messages)->dn_Type )
          {
             case DN_OPUS_QUIT :
                                 dd->stop = TRUE;
                                 break;
                                 
                                 
             case DN_CLOSE_WORKBENCH:                    
             case DN_OPUS_HIDE : // storing our current window state
                                 dd->flags = dd->win ? TRUE : FALSE;
                                                                  
                                 SendCxMsg( (CX_OBJ *) dd->our_cxobj,
                                            CXCMD_DISAPPEAR,
                                            dd->sender ); 
                                 break;
                                 
             case DN_OPEN_WORKBENCH:
             case DN_RESET_WORKBENCH:
             case DN_OPUS_SHOW :
                                 if( dd->flags )
                                      SendCxMsg( (CX_OBJ *) dd->our_cxobj,
                                                 CXCMD_APPEAR,
                                                 dd->sender ); 
                                 break;             
          } 
        
        ReplyFreeMsg( dd->messages );
     }
     
   return dd->stop;
}

BOOL HandleIPC( DOE_Data *dd )
{
   while( !dd->stop && (dd->messages = GetMsg(dd->ipc->command_port)) )
     {
        if( ((IPCMessage *) dd->messages)->command & IPCCMD_QUIT )
          {
             dd->stop = TRUE;
          }
        else        
          {
             if( ((IPCMessage *) dd->messages)->command & IPCCMD_SHOW )
               {
                  SendCxMsg((CX_OBJ *) dd->our_cxobj, CXCMD_APPEAR, dd->sender );                  
               }
               
             if( ((IPCMessage *) dd->messages)->command & IPCCMD_NAME )
               {
                  // make a copy...
                  strncpy( dd->name, ((PassData *) ((IPCMessage *) dd->messages)->data_free)->name, 11 )[11] = 0;
                  
                  // if the supplied name is empty, it does fallback to the buildin name
                  dd->itext.IText = strlen(dd->name) ? dd->name : DOpusGetString( locale, MSG_TEXT );
               }
                    
             if( ((IPCMessage *) dd->messages)->command & IPCCMD_BGC )
               {
                  dd->itext.BackPen = ((PassData *) ((IPCMessage *) dd->messages)->data_free)->BackPen;
               }
               
             if( ((IPCMessage *) dd->messages)->command & IPCCMD_TC )
               {
                  dd->itext.FrontPen = ((PassData *) ((IPCMessage *) dd->messages)->data_free)->FrontPen;
               }
                                              
             if( dd->win )
               {
                  // save our current position temporally
                  dd->ibox.Left = dd->win->LeftEdge;
                  dd->ibox.Top  = dd->win->TopEdge;
                       
                  if( ((IPCMessage *) dd->messages)->command & IPCCMD_NAME )
                    {
                       // changing the name does require closing and reopening
                       // of the window since the window size depends from the
                       // name... (and font)
                       
                       CloseWindow( dd->win );
                       OpenWin( dd );
                    }
                  
                  if( ((IPCMessage *) dd->messages)->command & (IPCCMD_BGC | IPCCMD_TC) )
                    {
                       BorderDraw( dd, FALSE );
                    }
                    
                  if( ((IPCMessage *) dd->messages)->command & IPCCMD_HIDE )
                    {                                     
                       SendCxMsg((CX_OBJ *) dd->our_cxobj, CXCMD_DISAPPEAR, dd->sender );                                                                 
                    }
               }           
          }
          
        IPC_Reply( dd->messages );       
     }
   
   return dd->stop;
}

/********************************************************************/

BOOL SendCxMsg( CX_OBJ *cxobj, ULONG type, struct MsgPort *reply_to )
{
   FakeMsg *fmsg;
   
   // create a fake CxMsg (not documented..., size is 232 bytes)
   // and send it to the choosed CxObj
   
   if( (fmsg = AllocMemH(mempool, 232)) )
     {
        fmsg->msg.mn_Node.ln_Type = NT_MESSAGE;
        fmsg->msg.mn_Length       = 232;
        fmsg->Code                = CXM_COMMAND;                                                   
        fmsg->msg.mn_ReplyPort    = reply_to;
        fmsg->ID                  = type;    
        
        PutMsg( cxobj->cx_port, (struct Message *) fmsg );
        
        return TRUE;
      }
      
   return FALSE;
}

/********************************************************************/
// saving/reading the config
// normally I use for this an own file, but since here is only to
// write some small values... :)

// the following include is only needed to use some defines of it
#include <libraries/iffparse.h>

// now we are doing some ID's 
// each data block, which should be recognizeable must have an ID
// (=header)

#define  DOE_ID           MAKE_ID('D','O','E','X')
#define  LEFT_TOP         MAKE_ID('L','E','T','E')
#define  COLOURS          MAKE_ID('C','O','L','R')
#define  NEW_NAME         MAKE_ID('N','A','M','E')

 
BOOL WriteConfig( DOE_Data *dd )
{
     APTR iffhandle;
     ULONG size;
     
     // open our config file for writing     
     if( (iffhandle = IFFOpen(CONFIG_FILE, IFF_WRITE, DOE_ID)) )
       {  
          // here you can see it is possible to write more than one value
          // with one step, they must only follow each other...          
          IFFWriteChunk( iffhandle, &dd->win->LeftEdge, LEFT_TOP, sizeof(WORD) * 2 );
          IFFWriteChunk( iffhandle, &dd->itext.FrontPen, COLOURS, sizeof(UBYTE) * 2 );
          
          if( (size = strlen(dd->name)) )
            {
               // it's a nice idea to write the \0 of the string too,
               // it does save us work on reading the data
               IFFWriteChunk( iffhandle, dd->name, NEW_NAME, size + 1 );
            }
            
          IFFClose( iffhandle );
          return 0;
        };
                 
     return 1;
}

// also simple to understand...

BOOL ReadConfig( DOE_Data *dd )
{
     APTR iffhandle;
     ULONG nextchunk;
     
     // open the config file for reading     
     if( (iffhandle = IFFOpen(CONFIG_FILE, IFF_READ, DOE_ID)) )
       {
          // IFFNextChunk() does return the next ID (or NULL if there is none)
          // and so we can switch here
          while( (nextchunk = IFFNextChunk(iffhandle, 0)) )
            {
                switch( nextchunk )
                  {                                                                                                                                
                       case LEFT_TOP: IFFReadChunkBytes(iffhandle, &dd->ibox, sizeof(WORD) * 2 );
                                      break;
                                      
                       case COLOURS : IFFReadChunkBytes(iffhandle, &dd->itext.FrontPen, sizeof(UBYTE) * 2 );
                                      break;
                                      
                       case NEW_NAME: // if a string should be readed, it is needed to use the
                                      // string alone in a chunk or at least to have it at the
                                      // end of a structure - or -
                                      // if you do this not, you must always write the whole
                                      // structure
                                      // if you try to read an alone written string, you
                                      // must always set the size to read to the lenght of
                                      // you string buffer
                                      
                                      IFFReadChunkBytes(iffhandle, dd->name, 16 );
                                      break;  
                  };
            };
          IFFClose( iffhandle );
               
          return 0;
         
       };
     return 1;
}